పైథాన్ అసింకియో లో-లెవల్ నెట్వర్కింగ్లో నైపుణ్యం సాధించండి. ఈ విశ్లేషణ ట్రాన్స్పోర్ట్లు, ప్రోటోకాల్లను, అధిక-పనితీరు గల నెట్వర్క్ అప్లికేషన్ల నిర్మాణానికి ఆచరణాత్మక ఉదాహరణలతో వివరిస్తుంది.
పైథాన్ అసింకియో ట్రాన్స్పోర్ట్ను సరళీకరించడం: లో-లెవల్ నెట్వర్కింగ్లోకి లోతైన విశ్లేషణ
ఆధునిక పైథాన్ ప్రపంచంలో, asyncio అధిక-పనితీరు గల నెట్వర్క్ ప్రోగ్రామింగ్కు మూలస్తంభంగా మారింది. డెవలపర్లు తరచుగా దీని అద్భుతమైన హై-లెవల్ APIలతో ప్రారంభిస్తారు, aiohttp లేదా FastAPI వంటి లైబ్రరీలతో async మరియు await ఉపయోగించి అద్భుతమైన సులువుగా ప్రతిస్పందించే అప్లికేషన్లను నిర్మిస్తారు. asyncio.open_connection() వంటి ఫంక్షన్ల ద్వారా అందించబడిన StreamReader మరియు StreamWriter వస్తువులు నెట్వర్క్ I/Oని నిర్వహించడానికి అద్భుతంగా సరళమైన, క్రమబద్ధమైన మార్గాన్ని అందిస్తాయి. అయితే సంగ్రహణ సరిపోనప్పుడు ఏమి జరుగుతుంది? సంక్లిష్టమైన, స్టేట్ఫుల్ లేదా ప్రామాణికం కాని నెట్వర్క్ ప్రోటోకాల్ను మీరు అమలు చేయవలసి వస్తే? మీరు అంతర్లీన కనెక్షన్ను నేరుగా నియంత్రించడం ద్వారా పనితీరు యొక్క ప్రతి చివరి చుక్కను బయటకు తీయవలసి వస్తే? ఇక్కడే asyncio యొక్క నెట్వర్కింగ్ సామర్థ్యాలకు నిజమైన పునాది ఉంది: లో-లెవల్ ట్రాన్స్పోర్ట్ మరియు ప్రోటోకాల్ API. ఇది మొదట్లో భయానకంగా అనిపించినప్పటికీ, ఈ శక్తివంతమైన ద్వయాన్ని అర్థం చేసుకోవడం నియంత్రణ మరియు వశ్యత యొక్క కొత్త స్థాయిని అన్లాక్ చేస్తుంది, మీరు ఊహించగలిగే దాదాపు ఏ నెట్వర్క్ అప్లికేషన్నైనా నిర్మించడానికి మిమ్మల్ని అనుమతిస్తుంది. ఈ సమగ్ర గైడ్ సంగ్రహణ యొక్క పొరలను విడదీస్తుంది, ట్రాన్స్పోర్ట్లు మరియు ప్రోటోకాల్ల మధ్య సహజీవన సంబంధాన్ని అన్వేషిస్తుంది మరియు పైథాన్లో లో-లెవల్ అసమకాలిక నెట్వర్కింగ్లో నైపుణ్యం సాధించడానికి మీకు ఆచరణాత్మక ఉదాహరణల ద్వారా మార్గనిర్దేశం చేస్తుంది.
అసింకియో నెట్వర్కింగ్ యొక్క రెండు ముఖాలు: హై-లెవల్ వర్సెస్ లో-లెవల్
లో-లెవల్ APIలలోకి లోతుగా వెళ్లే ముందు, అసింకియో ఎకోసిస్టమ్లో వాటి స్థానాన్ని అర్థం చేసుకోవడం చాలా ముఖ్యం. అసింకియో తెలివిగా నెట్వర్క్ కమ్యూనికేషన్ కోసం రెండు విభిన్న లేయర్లను అందిస్తుంది, ప్రతి ఒక్కటి వేర్వేరు వినియోగ సందర్భాల కోసం రూపొందించబడింది.
హై-లెవల్ API: స్ట్రీమ్లు
హై-లెవల్ API, సాధారణంగా "స్ట్రీమ్లు" అని పిలువబడుతుంది, చాలా మంది డెవలపర్లు మొదట ఎదుర్కొనేది ఇదే. మీరు asyncio.open_connection() లేదా asyncio.start_server()ని ఉపయోగించినప్పుడు, మీకు StreamReader మరియు StreamWriter ఆబ్జెక్ట్లు లభిస్తాయి. ఈ API సరళత మరియు వినియోగానికి సులువుగా ఉండేలా రూపొందించబడింది.
- కమాండ్ స్టైల్: ఇది క్రమబద్ధంగా కనిపించే కోడ్ను వ్రాయడానికి మిమ్మల్ని అనుమతిస్తుంది. 100 బైట్లను పొందడానికి మీరు
await reader.read(100)ని, ఆపై ప్రతిస్పందనను పంపడానికిwriter.write(data)ని ఉపయోగిస్తారు. ఈasync/awaitనమూనా సహజమైనది మరియు అర్థం చేసుకోవడం సులువు. - అనుకూలమైన సహాయకులు: ఇది
readuntil(separator)మరియుreadexactly(n)వంటి పద్ధతులను అందిస్తుంది, ఇవి సాధారణ ఫ్రేమింగ్ పనులను నిర్వహిస్తాయి, బఫర్లను మాన్యువల్గా నిర్వహించకుండా మిమ్మల్ని రక్షిస్తాయి. - ఆదర్శ వినియోగ సందర్భాలు: సాధారణ అభ్యర్థన-ప్రతిస్పందన ప్రోటోకాల్ల (ప్రాథమిక HTTP క్లయింట్ వంటివి), లైన్-ఆధారిత ప్రోటోకాల్ల (Redis లేదా SMTP వంటివి) లేదా కమ్యూనికేషన్ ఊహించదగిన, సరళ ప్రవాహాన్ని అనుసరించే ఏదైనా పరిస్థితికి సరైనది.
అయితే, ఈ సరళతతో ఒక లోపం వస్తుంది. అయాచిత సందేశాలు ఎప్పుడైనా రాగల అత్యంత సమకాలీన, ఈవెంట్-ఆధారిత ప్రోటోకాల్ల కోసం స్ట్రీమ్-ఆధారిత విధానం తక్కువ సమర్థవంతంగా ఉంటుంది. సీక్వెన్షియల్ await మోడల్ ఏకకాల రీడ్లు మరియు రైట్లను నిర్వహించడం లేదా సంక్లిష్ట కనెక్షన్ స్టేట్లను నిర్వహించడం కష్టతరం చేస్తుంది.
లో-లెవల్ API: ట్రాన్స్పోర్ట్లు మరియు ప్రోటోకాల్లు
హై-లెవల్ స్ట్రీమ్స్ API వాస్తవానికి నిర్మించబడిన పునాది లేయర్ ఇదే. లో-లెవల్ API రెండు విభిన్న భాగాలపై ఆధారపడిన డిజైన్ నమూనాని ఉపయోగిస్తుంది: ట్రాన్స్పోర్ట్లు మరియు ప్రోటోకాల్లు.
- ఈవెంట్-ఆధారిత శైలి: డేటాను పొందడానికి మీరు ఒక ఫంక్షన్ను కాల్ చేయడానికి బదులుగా, ఈవెంట్లు సంభవించినప్పుడు (ఉదాహరణకు, కనెక్షన్ ఏర్పడినప్పుడు, డేటా స్వీకరించబడినప్పుడు) అసింకియో మీ ఆబ్జెక్ట్లోని పద్ధతులను పిలుస్తుంది. ఇది కాల్బ్యాక్-ఆధారిత విధానం.
- ఆందోళనల విభజన: ఇది "ఏమి" నుండి "ఎలా"ని స్పష్టంగా వేరు చేస్తుంది. ప్రోటోకాల్ డేటాతో ఏమి చేయాలో (మీ అప్లికేషన్ లాజిక్) నిర్వచిస్తుంది, అయితే ట్రాన్స్పోర్ట్ డేటాను నెట్వర్క్లో ఎలా పంపబడుతుంది మరియు స్వీకరించబడుతుంది (I/O మెకానిజం) అని నిర్వహిస్తుంది.
- గరిష్ట నియంత్రణ: ఈ API బఫరింగ్, ఫ్లో కంట్రోల్ (బ్యాక్ప్రెషర్) మరియు కనెక్షన్ లైఫ్సైకిల్పై మీకు నిశితమైన నియంత్రణను అందిస్తుంది.
- ఆదర్శ వినియోగ సందర్భాలు: కస్టమ్ బైనరీ లేదా టెక్స్ట్ ప్రోటోకాల్లను అమలు చేయడానికి, వేల సంఖ్యలో నిరంతర కనెక్షన్లను నిర్వహించే అధిక-పనితీరు గల సర్వర్లను నిర్మించడానికి లేదా నెట్వర్క్ ఫ్రేమ్వర్క్లు మరియు లైబ్రరీలను అభివృద్ధి చేయడానికి ఇది అవసరం.
దీనిని ఇలా ఆలోచించండి: స్ట్రీమ్స్ API ఒక మీల్ కిట్ సర్వీస్ను ఆర్డర్ చేసినట్లు. మీకు ముందుగా విభజించిన పదార్థాలు మరియు అనుసరించడానికి ఒక సాధారణ వంటకం లభిస్తుంది. ట్రాన్స్పోర్ట్ మరియు ప్రోటోకాల్ API అనేది ఒక వృత్తిపరమైన వంటగదిలో ముడి పదార్థాలతో మరియు ప్రక్రియ యొక్క ప్రతి దశపై పూర్తి నియంత్రణతో ఒక చెఫ్గా ఉన్నట్లు. రెండూ గొప్ప భోజనాన్ని ఉత్పత్తి చేయగలవు, కానీ తరువాతిది అపరిమిత సృజనాత్మకత మరియు నియంత్రణను అందిస్తుంది.
ప్రధాన భాగాలు: ట్రాన్స్పోర్ట్లు మరియు ప్రోటోకాల్లపై ఒక నిశిత పరిశీలన
లో-లెవల్ API యొక్క శక్తి ప్రోటోకాల్ మరియు ట్రాన్స్పోర్ట్ మధ్య సొగసైన పరస్పర చర్య నుండి వస్తుంది. అవి ఏ లో-లెవల్ అసింకియో నెట్వర్క్ అప్లికేషన్లోనైనా విభిన్నమైన కానీ విడదీయరాని భాగస్వాములు.
ప్రోటోకాల్: మీ అప్లికేషన్ యొక్క మెదడు
ప్రోటోకాల్ అనేది మీరు వ్రాసే ఒక క్లాస్. ఇది asyncio.Protocol (లేదా దానిలోని ఒక వేరియంట్) నుండి వారసత్వాన్ని పొందుతుంది మరియు ఒకే నెట్వర్క్ కనెక్షన్ను నిర్వహించడానికి స్టేట్ మరియు లాజిక్ను కలిగి ఉంటుంది. మీరు ఈ క్లాస్ను మీరే ఇన్స్టాంటియేట్ చేయరు; మీరు దానిని అసింకియోకు అందిస్తారు (ఉదాహరణకు, loop.create_serverకు), మరియు అసింకియో ప్రతి కొత్త క్లయింట్ కనెక్షన్ కోసం మీ ప్రోటోకాల్ యొక్క కొత్త ఇన్స్టాన్స్ను సృష్టిస్తుంది.
మీ ప్రోటోకాల్ క్లాస్ ఈవెంట్ హ్యాండ్లర్ పద్ధతుల సమితి ద్వారా నిర్వచించబడుతుంది, వీటిని ఈవెంట్ లూప్ కనెక్షన్ లైఫ్సైకిల్లోని వివిధ పాయింట్ల వద్ద పిలుస్తుంది. వీటిలో అత్యంత ముఖ్యమైనవి:
connection_made(self, transport)
కొత్త కనెక్షన్ విజయవంతంగా స్థాపించబడినప్పుడు ఒక్కసారి మాత్రమే పిలవబడుతుంది. ఇది మీ ఎంట్రీ పాయింట్. ఇక్కడే మీరు transport ఆబ్జెక్ట్ను స్వీకరిస్తారు, ఇది కనెక్షన్ను సూచిస్తుంది. మీరు దానిని ఎల్లప్పుడూ రిఫరెన్స్గా, సాధారణంగా self.transportగా సేవ్ చేయాలి. ఇది బఫర్లను సెటప్ చేయడం లేదా పీర్ యొక్క అడ్రస్ను లాగ్ చేయడం వంటి ఏ కనెక్షన్కు సంబంధించిన ఇనిషియలైజేషన్నైనా నిర్వహించడానికి ఆదర్శవంతమైన ప్రదేశం.
data_received(self, data)
మీ ప్రోటోకాల్ యొక్క గుండె. కనెక్షన్ యొక్క మరొక చివర నుండి కొత్త డేటా స్వీకరించబడినప్పుడల్లా ఈ పద్ధతి పిలవబడుతుంది. data ఆర్గ్యుమెంట్ ఒక bytes ఆబ్జెక్ట్. TCP ఒక స్ట్రీమ్ ప్రోటోకాల్, మెసేజ్ ప్రోటోకాల్ కాదు అని గుర్తుంచుకోవడం చాలా ముఖ్యం. మీ అప్లికేషన్ నుండి ఒకే లాజికల్ మెసేజ్ అనేక data_received కాల్లలో విభజించబడవచ్చు, లేదా అనేక చిన్న మెసేజ్లు ఒకే కాల్లోకి బండిల్ చేయబడవచ్చు. ఈ బఫరింగ్ మరియు పార్సింగ్ను మీ కోడ్ నిర్వహించాలి.
connection_lost(self, exc)
కనెక్షన్ మూసివేయబడినప్పుడు పిలవబడుతుంది. ఇది అనేక కారణాల వల్ల జరగవచ్చు. కనెక్షన్ శుభ్రంగా మూసివేయబడితే (ఉదాహరణకు, మరొక వైపు మూసివేస్తుంది, లేదా మీరు transport.close()ని కాల్ చేస్తే), exc None అవుతుంది. లోపం కారణంగా కనెక్షన్ మూసివేయబడితే (ఉదాహరణకు, నెట్వర్క్ వైఫల్యం, రీసెట్), exc లోపాన్ని వివరించే ఎక్సెప్షన్ ఆబ్జెక్ట్ అవుతుంది. క్లీనప్ చేయడానికి, డిస్కనెక్షన్ను లాగ్ చేయడానికి లేదా మీరు క్లయింట్ను నిర్మిస్తున్నట్లయితే మళ్లీ కనెక్ట్ అవ్వడానికి ప్రయత్నించడానికి ఇది మీకు అవకాశం.
eof_received(self)
ఇది మరింత సూక్ష్మమైన కాల్బ్యాక్. మరొక చివర ఇక డేటాను పంపదని సంకేతం ఇచ్చినప్పుడు (ఉదాహరణకు, POSIX సిస్టమ్లో shutdown(SHUT_WR)ని కాల్ చేయడం ద్వారా) పిలవబడుతుంది, అయితే కనెక్షన్ మీకు డేటాను పంపడానికి ఇంకా తెరిచి ఉండవచ్చు. మీరు ఈ పద్ధతి నుండి Trueని తిరిగి ఇస్తే, ట్రాన్స్పోర్ట్ మూసివేయబడుతుంది. మీరు Falseని తిరిగి ఇస్తే (డిఫాల్ట్), ట్రాన్స్పోర్ట్ను మీరు స్వయంగా తర్వాత మూసివేయడానికి బాధ్యత వహించాలి.
ట్రాన్స్పోర్ట్: కమ్యూనికేషన్ ఛానెల్
ట్రాన్స్పోర్ట్ అనేది అసింకియో ద్వారా అందించబడిన ఒక ఆబ్జెక్ట్. మీరు దానిని సృష్టించరు; మీరు దానిని మీ ప్రోటోకాల్ యొక్క connection_made పద్ధతిలో స్వీకరిస్తారు. ఇది అంతర్లీన నెట్వర్క్ సాకెట్ మరియు ఈవెంట్ లూప్ యొక్క I/O షెడ్యూలింగ్ పైన ఒక హై-లెవల్ అబ్స్ట్రాక్షన్గా పనిచేస్తుంది. దీని ప్రాథమిక పని డేటాను పంపడం మరియు కనెక్షన్ను నియంత్రించడం.
మీరు ట్రాన్స్పోర్ట్తో దాని పద్ధతుల ద్వారా సంకర్షణ చెందుతారు:
transport.write(data)
డేటాను పంపడానికి ప్రాథమిక పద్ధతి. data ఒక bytes ఆబ్జెక్ట్ అయి ఉండాలి. ఈ పద్ధతి నాన్-బ్లాకింగ్. ఇది వెంటనే డేటాను పంపదు. బదులుగా, ఇది డేటాను అంతర్గత రైట్ బఫర్లో ఉంచుతుంది, మరియు ఈవెంట్ లూప్ దానిని వీలైనంత సమర్థవంతంగా బ్యాక్గ్రౌండ్లో నెట్వర్క్లో పంపుతుంది.
transport.writelines(list_of_data)
ఒకేసారి బఫర్కు bytes ఆబ్జెక్ట్ల క్రమాన్ని వ్రాయడానికి మరింత సమర్థవంతమైన మార్గం, సిస్టమ్ కాల్ల సంఖ్యను సంభావ్యంగా తగ్గిస్తుంది.
transport.close()
ఇది హుందాగా షట్డౌన్ను ప్రారంభిస్తుంది. ట్రాన్స్పోర్ట్ ముందుగా దాని రైట్ బఫర్లో మిగిలి ఉన్న ఏదైనా డేటాను ఫ్లష్ చేస్తుంది, ఆపై కనెక్షన్ను మూసివేస్తుంది. close()ని కాల్ చేసిన తర్వాత ఇక డేటాను వ్రాయలేము.
transport.abort()
ఇది హార్డ్ షట్డౌన్ను నిర్వహిస్తుంది. కనెక్షన్ వెంటనే మూసివేయబడుతుంది మరియు రైట్ బఫర్లో పెండింగ్లో ఉన్న ఏదైనా డేటా విస్మరించబడుతుంది. దీనిని అసాధారణ పరిస్థితులలో ఉపయోగించాలి.
transport.get_extra_info(name, default=None)
ఆత్మపరిశీలన కోసం చాలా ఉపయోగకరమైన పద్ధతి. మీరు కనెక్షన్ గురించి సమాచారాన్ని పొందవచ్చు, ఉదాహరణకు పీర్ అడ్రస్ ('peername'), అంతర్లీన సాకెట్ ఆబ్జెక్ట్ ('socket'), లేదా SSL/TLS సర్టిఫికేట్ సమాచారం ('ssl_object').
సహజీవన సంబంధం
ఈ డిజైన్ యొక్క అందం సమాచారం యొక్క స్పష్టమైన, చక్రీయ ప్రవాహం:
- సెటప్: ఈవెంట్ లూప్ ఒక కొత్త కనెక్షన్ను అంగీకరిస్తుంది.
- ఇన్స్టాంటియేషన్: లూప్ మీ
Protocolక్లాస్ యొక్క ఇన్స్టాన్స్ మరియు కనెక్షన్ను సూచించేTransportఆబ్జెక్ట్ను సృష్టిస్తుంది. - లింకేజ్: లూప్
your_protocol.connection_made(transport)ని పిలుస్తుంది, రెండు ఆబ్జెక్ట్లను కలిపి లింక్ చేస్తుంది. మీ ప్రోటోకాల్ ఇప్పుడు డేటాను పంపే మార్గాన్ని కలిగి ఉంది. - డేటాను స్వీకరించడం: నెట్వర్క్ సాకెట్పై డేటా వచ్చినప్పుడు, ఈవెంట్ లూప్ మేల్కొని, డేటాను చదువుతుంది మరియు
your_protocol.data_received(data)ని పిలుస్తుంది. - ప్రాసెసింగ్: మీ ప్రోటోకాల్ యొక్క లాజిక్ స్వీకరించబడిన డేటాను ప్రాసెస్ చేస్తుంది.
- డేటాను పంపడం: దాని లాజిక్ ఆధారంగా, మీ ప్రోటోకాల్
self.transport.write(response_data)ని పిలిచి ప్రత్యుత్తరం పంపుతుంది. డేటా బఫర్ చేయబడుతుంది. - బ్యాక్గ్రౌండ్ I/O: ఈవెంట్ లూప్ ట్రాన్స్పోర్ట్ ద్వారా బఫర్ చేయబడిన డేటాను నాన్-బ్లాకింగ్ పంపడాన్ని నిర్వహిస్తుంది.
- టీయర్డౌన్: కనెక్షన్ ముగిసినప్పుడు, ఈవెంట్ లూప్
your_protocol.connection_lost(exc)ని చివరి క్లీనప్ కోసం పిలుస్తుంది.
ఆచరణాత్మక ఉదాహరణను నిర్మించడం: ఒక ఎకో సర్వర్ మరియు క్లయింట్
సిద్ధాంతం గొప్పది, కానీ ట్రాన్స్పోర్ట్లు మరియు ప్రోటోకాల్లను అర్థం చేసుకోవడానికి ఉత్తమ మార్గం ఏదైనా నిర్మించడం. ఒక క్లాసిక్ ఎకో సర్వర్ను మరియు దానికి సంబంధించిన క్లయింట్ను సృష్టిద్దాం. సర్వర్ కనెక్షన్లను అంగీకరిస్తుంది మరియు స్వీకరించిన ఏదైనా డేటాను తిరిగి పంపుతుంది.
ఎకో సర్వర్ అమలు
ముందుగా, మేము మా సర్వర్-సైడ్ ప్రోటోకాల్ను నిర్వచిస్తాము. ఇది చాలా సరళమైనది, ప్రధాన ఈవెంట్ హ్యాండ్లర్లను ప్రదర్శిస్తుంది.
import asyncio
class EchoServerProtocol(asyncio.Protocol):
def connection_made(self, transport):
# ఒక కొత్త కనెక్షన్ స్థాపించబడింది.
# లాగింగ్ కోసం రిమోట్ అడ్రస్ను పొందండి.
peername = transport.get_extra_info('peername')
print(f"Connection from: {peername}")
# తర్వాత ఉపయోగం కోసం ట్రాన్స్పోర్ట్ను నిల్వ చేయండి.
self.transport = transport
def data_received(self, data):
# క్లయింట్ నుండి డేటా స్వీకరించబడింది.
message = data.decode()
print(f"Data received: {message.strip()}")
# క్లయింట్కు డేటాను తిరిగి ప్రతిధ్వనించండి.
print(f"Echoing back: {message.strip()}")
self.transport.write(data)
def connection_lost(self, exc):
# కనెక్షన్ మూసివేయబడింది.
print("Connection closed.")
# ట్రాన్స్పోర్ట్ స్వయంచాలకంగా మూసివేయబడుతుంది, ఇక్కడ self.transport.close()ని కాల్ చేయనవసరం లేదు.
async def main_server():
# సర్వర్ను నిరవధికంగా నడపడానికి మేము ప్లాన్ చేస్తున్నందున ఈవెంట్ లూప్కు ఒక రిఫరెన్స్ను పొందండి.
loop = asyncio.get_running_loop()
host = '127.0.0.1'
port = 8888
# The `create_server` కొరౌటిన్ సర్వర్ను సృష్టిస్తుంది మరియు ప్రారంభిస్తుంది.
# మొదటి ఆర్గ్యుమెంట్ protocol_factory, ఇది కొత్త ప్రోటోకాల్ ఇన్స్టాన్స్ను తిరిగి ఇచ్చే కాల్ చేయదగినది.
# మా విషయంలో, `EchoServerProtocol` క్లాస్ను పాస్ చేస్తే సరిపోతుంది.
server = await loop.create_server(
lambda: EchoServerProtocol(),
host,
port)
addrs = ', '.join(str(sock.getsockname()) for sock in server.sockets)
print(f'Serving on {addrs}')
# సర్వర్ బ్యాక్గ్రౌండ్లో నడుస్తుంది. ప్రధాన కొరౌటిన్ను సజీవంగా ఉంచడానికి,
# మేము కొత్త ఫ్యూచర్ లాగా, ఎప్పటికీ పూర్తికాని దాని కోసం వేచి ఉండవచ్చు.
# ఈ ఉదాహరణ కోసం, మేము దానిని "ఎప్పటికీ" నడుపుతాము.
async with server:
await server.serve_forever()
if __name__ == "__main__":
try:
# సర్వర్ను నడపడానికి:
asyncio.run(main_server())
except KeyboardInterrupt:
print("Server shut down.")
ఈ సర్వర్ కోడ్లో, loop.create_server() కీలకం. ఇది పేర్కొన్న హోస్ట్ మరియు పోర్ట్కు బైండ్ అవుతుంది మరియు కొత్త కనెక్షన్ల కోసం వినడం ప్రారంభించమని ఈవెంట్ లూప్కు చెబుతుంది. ప్రతి ఇన్కమింగ్ కనెక్షన్ కోసం, అది మా protocol_factoryని (lambda: EchoServerProtocol() ఫంక్షన్) పిలుస్తుంది, ఆ నిర్దిష్ట క్లయింట్ కోసం ఒక కొత్త ప్రోటోకాల్ ఇన్స్టాన్స్ను సృష్టించడానికి.
ఎకో క్లయింట్ అమలు
క్లయింట్ ప్రోటోకాల్ కొద్దిగా సంక్లిష్టమైనది, ఎందుకంటే అది దాని స్వంత స్థితిని నిర్వహించాలి: ఏ సందేశాన్ని పంపాలి మరియు దాని పని "పూర్తయింది" అని ఎప్పుడు పరిగణించాలి. క్లయింట్ను ప్రారంభించిన ప్రధాన కొరౌటిన్కు పూర్తి చేసినట్లు సంకేతం ఇవ్వడానికి asyncio.Future లేదా asyncio.Eventని ఉపయోగించడం ఒక సాధారణ నమూనా.
import asyncio
class EchoClientProtocol(asyncio.Protocol):
def __init__(self, message, on_con_lost):
self.message = message
self.on_con_lost = on_con_lost
self.transport = None
def connection_made(self, transport):
self.transport = transport
print(f"Sending: {self.message}")
self.transport.write(self.message.encode())
def data_received(self, data):
print(f"Received echo: {data.decode().strip()}")
def connection_lost(self, exc):
print("The server closed the connection")
# కనెక్షన్ పోయింది మరియు పని పూర్తయింది అని సంకేతం ఇవ్వండి.
self.on_con_lost.set_result(True)
def eof_received(self):
# సర్వర్ మూసివేయడానికి ముందు EOF పంపితే దీనిని పిలవవచ్చు.
print("Received EOF from server.")
async def main_client():
loop = asyncio.get_running_loop()
# క్లయింట్ పని పూర్తి అయినట్లు సంకేతం ఇవ్వడానికి on_con_lost ఫ్యూచర్ ఉపయోగించబడుతుంది.
on_con_lost = loop.create_future()
message = "Hello World!"
host = '127.0.0.1'
port = 8888
# `create_connection` కనెక్షన్ను ఏర్పాటు చేస్తుంది మరియు ప్రోటోకాల్ను లింక్ చేస్తుంది.
try:
transport, protocol = await loop.create_connection(
lambda: EchoClientProtocol(message, on_con_lost),
host,
port)
except ConnectionRefusedError:
print("Connection refused. Is the server running?")
return
# కనెక్షన్ పోయింది అని ప్రోటోకాల్ సంకేతం ఇచ్చే వరకు వేచి ఉండండి.
try:
await on_con_lost
finally:
# ట్రాన్స్పోర్ట్ను హుందాగా మూసివేయండి.
transport.close()
if __name__ == "__main__":
# క్లయింట్ను నడపడానికి:
# ముందుగా, ఒక టెర్మినల్లో సర్వర్ను ప్రారంభించండి.
# ఆపై, మరొక టెర్మినల్లో ఈ స్క్రిప్ట్ను అమలు చేయండి.
asyncio.run(main_client())
ఇక్కడ, loop.create_connection() అనేది create_serverకు క్లయింట్-సైడ్ ప్రతిరూపం. ఇది ఇచ్చిన చిరునామాకు కనెక్ట్ అవ్వడానికి ప్రయత్నిస్తుంది. విజయవంతమైతే, అది మా EchoClientProtocolను ఇన్స్టాంటియేట్ చేస్తుంది మరియు దాని connection_made పద్ధతిని పిలుస్తుంది. on_con_lost ఫ్యూచర్ వినియోగం ఒక క్లిష్టమైన నమూనా. main_client కొరౌటిన్ ఈ ఫ్యూచర్కు await చేస్తుంది, connection_lostలోపల నుండి on_con_lost.set_result(True)ని కాల్ చేయడం ద్వారా దాని పని పూర్తయిందని ప్రోటోకాల్ సంకేతం ఇచ్చే వరకు దాని స్వంత అమలును సమర్థవంతంగా నిలిపివేస్తుంది.
అధునాతన భావనలు మరియు నిజ-ప్రపంచ దృశ్యాలు
ఎకో ఉదాహరణ ప్రాథమిక అంశాలను కవర్ చేస్తుంది, కానీ నిజ-ప్రపంచ ప్రోటోకాల్లు అరుదుగా అంత సరళంగా ఉంటాయి. మీరు అనివార్యంగా ఎదుర్కొనే కొన్ని అధునాతన అంశాలను అన్వేషిద్దాం.
సందేశ ఫ్రేమింగ్ మరియు బఫరింగ్ నిర్వహణ
ప్రాథమిక అంశాల తర్వాత గ్రహించాల్సిన అత్యంత ముఖ్యమైన భావన ఏమిటంటే TCP అనేది బైట్ల స్ట్రీమ్. అంతర్గత "సందేశ" సరిహద్దులు లేవు. ఒక క్లయింట్ "Hello" ఆపై "World" పంపినట్లయితే, మీ సర్వర్ యొక్క data_received ఒకసారి b'HelloWorld'తో, రెండుసార్లు b'Hello' మరియు b'World'తో, లేదా పాక్షిక డేటాతో అనేక సార్లు కూడా పిలవబడవచ్చు.
మీ ప్రోటోకాల్ "ఫ్రేమింగ్"కు బాధ్యత వహిస్తుంది — ఈ బైట్ స్ట్రీమ్లను అర్థవంతమైన సందేశాలుగా తిరిగి కలపడం. న్యూలైన్ క్యారెక్టర్ (\n) వంటి డెలిమిటర్ను ఉపయోగించడం ఒక సాధారణ వ్యూహం.
న్యూలైన్ కనుగొనే వరకు డేటాను బఫర్ చేసే, ఒకేసారి ఒక లైన్ను ప్రాసెస్ చేసే సవరించిన ప్రోటోకాల్ ఇక్కడ ఉంది.
class LineBasedProtocol(asyncio.Protocol):
def __init__(self):
self._buffer = b''
self.transport = None
def connection_made(self, transport):
self.transport = transport
print("Connection established.")
def data_received(self, data):
# అంతర్గత బఫర్కు కొత్త డేటాను జోడించండి
self._buffer += data
# బఫర్లో ఉన్నంత వరకు పూర్తి లైన్లను ప్రాసెస్ చేయండి
while b'\n' in self._buffer:
line, self._buffer = self._buffer.split(b'\n', 1)
self.process_line(line.decode().strip())
def process_line(self, line):
# ఇక్కడే ఒకే సందేశం కోసం మీ అప్లికేషన్ లాజిక్ ఉంటుంది
print(f"Processing complete message: {line}")
response = f"Processed: {line}\n"
self.transport.write(response.encode())
def connection_lost(self, exc):
print("Connection lost.")
ఫ్లో కంట్రోల్ (బ్యాక్ప్రెషర్) నిర్వహణ
మీ అప్లికేషన్ నెట్వర్క్ లేదా రిమోట్ పీర్ నిర్వహించగలిగే దానికంటే వేగంగా డేటాను ట్రాన్స్పోర్ట్కు వ్రాస్తుంటే ఏమి జరుగుతుంది? డేటా ట్రాన్స్పోర్ట్ యొక్క అంతర్గత బఫర్లో పేరుకుపోతుంది. ఇది నిరంతరం తనిఖీ చేయకుండా కొనసాగితే, బఫర్ నిరవధికంగా పెరిగి, అందుబాటులో ఉన్న మొత్తం మెమరీని వినియోగించుకుంటుంది. ఈ సమస్యను "బ్యాక్ప్రెషర్" లేకపోవడం అంటారు.
దీనిని నిర్వహించడానికి అసింకియో ఒక విధానాన్ని అందిస్తుంది. ట్రాన్స్పోర్ట్ దాని స్వంత బఫర్ పరిమాణాన్ని పర్యవేక్షిస్తుంది. బఫర్ ఒక నిర్దిష్ట హై-వాటర్ మార్క్ను దాటి పెరిగినప్పుడు, ఈవెంట్ లూప్ మీ ప్రోటోకాల్ యొక్క pause_writing() పద్ధతిని పిలుస్తుంది. ఇది మీ అప్లికేషన్కు డేటాను పంపడం ఆపమని ఒక సంకేతం. బఫర్ ఒక లో-వాటర్ మార్క్ కంటే తక్కువకు పంపబడినప్పుడు, లూప్ resume_writing()ని పిలుస్తుంది, డేటాను మళ్లీ పంపడం సురక్షితమని సంకేతం ఇస్తుంది.
class FlowControlledProtocol(asyncio.Protocol):
def __init__(self):
self._paused = False
self._data_source = some_data_generator() # డేటా మూలాన్ని ఊహించుకోండి
self.transport = None
def connection_made(self, transport):
self.transport = transport
self.resume_writing() # రైటింగ్ ప్రక్రియను ప్రారంభించండి
def pause_writing(self):
# ట్రాన్స్పోర్ట్ బఫర్ నిండిపోయింది.
print("Pausing writing.")
self._paused = True
def resume_writing(self):
# ట్రాన్స్పోర్ట్ బఫర్ ఖాళీ అయ్యింది.
print("Resuming writing.")
self._paused = False
self._write_more_data()
def _write_more_data(self):
# ఇది మా అప్లికేషన్ యొక్క రైట్ లూప్.
while not self._paused:
try:
data = next(self._data_source)
self.transport.write(data)
except StopIteration:
self.transport.close()
break # పంపడానికి డేటా లేదు
# వెంటనే పాజ్ చేయాలా వద్దా అని చూడటానికి బఫర్ పరిమాణాన్ని తనిఖీ చేయండి
if self.transport.get_write_buffer_size() > 0:
self.pause_writing()
TCPకి మించి: ఇతర ట్రాన్స్పోర్ట్లు
TCP అత్యంత సాధారణ వినియోగ సందర్భం అయినప్పటికీ, ట్రాన్స్పోర్ట్/ప్రోటోకాల్ నమూనా దీనికి మాత్రమే పరిమితం కాదు. అసింకియో ఇతర కమ్యూనికేషన్ రకాల కోసం సంగ్రహణలను అందిస్తుంది:
- UDP: కనెక్షన్ లేకుండా కమ్యూనికేషన్ కోసం, మీరు
loop.create_datagram_endpoint()ని ఉపయోగిస్తారు. ఇది మీకుDatagramTransportను ఇస్తుంది మరియు మీరుdatagram_received(data, addr)మరియుerror_received(exc)వంటి పద్ధతులతోasyncio.DatagramProtocolను అమలు చేస్తారు. - SSL/TLS: ఎన్క్రిప్షన్ జోడించడం చాలా సులభం. మీరు
ssl.SSLContextఆబ్జెక్ట్నుloop.create_server()లేదాloop.create_connection()కి పంపుతారు. అసింకియో TLS హ్యాండ్షేక్ను స్వయంచాలకంగా నిర్వహిస్తుంది మరియు మీకు సురక్షితమైన ట్రాన్స్పోర్ట్ లభిస్తుంది. మీ ప్రోటోకాల్ కోడ్ అస్సలు మార్చాల్సిన అవసరం లేదు. - సబ్ప్రొసెస్లు: పిల్లల ప్రక్రియలతో వాటి ప్రామాణిక I/O పైప్ల ద్వారా కమ్యూనికేట్ చేయడానికి,
loop.subprocess_exec()మరియుloop.subprocess_shell()నుasyncio.SubprocessProtocolతో ఉపయోగించవచ్చు. ఇది పిల్లల ప్రక్రియలను పూర్తిగా అసమకాలిక, నాన్-బ్లాకింగ్ పద్ధతిలో నిర్వహించడానికి మిమ్మల్ని అనుమతిస్తుంది.
వ్యూహాత్మక నిర్ణయం: ట్రాన్స్పోర్ట్లు వర్సెస్ స్ట్రీమ్లను ఎప్పుడు ఉపయోగించాలి
మీకు అందుబాటులో ఉన్న రెండు శక్తివంతమైన APIలతో, సరైన పని కోసం సరైనదాన్ని ఎంచుకోవడం ఒక కీలకమైన నిర్మాణ నిర్ణయం. మీరు నిర్ణయించుకోవడానికి సహాయపడే ఒక గైడ్ ఇక్కడ ఉంది.
స్ట్రీమ్లను (StreamReader/StreamWriter) ఎంచుకోండి...
- మీ ప్రోటోకాల్ సరళమైనది మరియు అభ్యర్థన-ప్రతిస్పందన ఆధారితమైనది. లాజిక్ "అభ్యర్థనను చదవండి, దాన్ని ప్రాసెస్ చేయండి, ప్రతిస్పందనను వ్రాయండి" అయితే, స్ట్రీమ్లు సరైనవి.
- మీరు బాగా తెలిసిన, లైన్-ఆధారిత లేదా స్థిర-పొడవు సందేశ ప్రోటోకాల్ కోసం క్లయింట్ను నిర్మిస్తున్నారు. ఉదాహరణకు, ఒక Redis సర్వర్తో లేదా ఒక సాధారణ FTP సర్వర్తో సంకర్షణ చెందడం.
- మీరు కోడ్ రీడబిలిటీ మరియు సరళ, కమాండ్ స్టైల్కు ప్రాధాన్యత ఇస్తారు. స్ట్రీమ్లతో
async/awaitసింటాక్స్ అసమకాలిక ప్రోగ్రామింగ్కు కొత్తగా ఉన్న డెవలపర్లకు అర్థం చేసుకోవడం తరచుగా సులభం. - వేగవంతమైన ప్రోటోటైపింగ్ కీలకం. మీరు కేవలం కొన్ని పంక్తుల కోడ్తో స్ట్రీమ్లతో ఒక సాధారణ క్లయింట్ లేదా సర్వర్ను ప్రారంభించి అమలు చేయవచ్చు.
ట్రాన్స్పోర్ట్లు మరియు ప్రోటోకాల్లను ఎంచుకోండి...
- మీరు సంక్లిష్టమైన లేదా కస్టమ్ నెట్వర్క్ ప్రోటోకాల్ను మొదటి నుండి అమలు చేస్తున్నారు. ఇది ప్రాథమిక వినియోగ సందర్భం. గేమింగ్, ఆర్థిక డేటా ఫీడ్లు, IoT పరికరాలు లేదా పీర్-టు-పీర్ అప్లికేషన్ల కోసం ప్రోటోకాల్లను ఆలోచించండి.
- మీ ప్రోటోకాల్ అత్యంత ఈవెంట్-ఆధారితమైనది మరియు పూర్తిగా అభ్యర్థన-ప్రతిస్పందన కాదు. సర్వర్ ఏ సమయంలోనైనా క్లయింట్కు అయాచిత సందేశాలను పంపగలిగితే, ప్రోటోకాల్ల యొక్క కాల్బ్యాక్-ఆధారిత స్వభావం మరింత సహజంగా సరిపోతుంది.
- మీకు గరిష్ట పనితీరు మరియు కనిష్ట ఓవర్హెడ్ అవసరం. ప్రోటోకాల్లు మీకు ఈవెంట్ లూప్కు మరింత ప్రత్యక్ష మార్గాన్ని అందిస్తాయి, స్ట్రీమ్స్ APIతో అనుబంధించబడిన కొన్ని ఓవర్హెడ్ను దాటవేస్తాయి.
- మీకు కనెక్షన్పై నిశితమైన నియంత్రణ అవసరం. ఇందులో మాన్యువల్ బఫర్ నిర్వహణ, స్పష్టమైన ఫ్లో కంట్రోల్ (
pause/resume_writing), మరియు కనెక్షన్ లైఫ్సైకిల్ యొక్క వివరణాత్మక నిర్వహణ ఉంటాయి. - మీరు ఒక నెట్వర్క్ ఫ్రేమ్వర్క్ లేదా లైబ్రరీని నిర్మిస్తున్నారు. మీరు ఇతర డెవలపర్ల కోసం ఒక సాధనాన్ని అందిస్తున్నట్లయితే, ప్రోటోకాల్/ట్రాన్స్పోర్ట్ API యొక్క పటిష్టమైన మరియు సౌకర్యవంతమైన స్వభావం తరచుగా సరైన పునాది.
ముగింపు: అసింకియో యొక్క పునాదిని స్వీకరించడం
పైథాన్ యొక్క asyncio లైబ్రరీ లేయర్డ్ డిజైన్లో ఒక మాస్టర్పీస్. హై-లెవల్ స్ట్రీమ్స్ API అందుబాటులో మరియు ఉత్పాదక ఎంట్రీ పాయింట్ను అందిస్తుండగా, asyncio యొక్క నెట్వర్కింగ్ సామర్థ్యాలకు నిజమైన, శక్తివంతమైన పునాదిని సూచించేది లో-లెవల్ ట్రాన్స్పోర్ట్ మరియు ప్రోటోకాల్ API. I/O మెకానిజం (ట్రాన్స్పోర్ట్)ను అప్లికేషన్ లాజిక్ (ప్రోటోకాల్) నుండి వేరు చేయడం ద్వారా, ఇది అధునాతన నెట్వర్క్ అప్లికేషన్లను నిర్మించడానికి పటిష్టమైన, స్కేలబుల్ మరియు అద్భుతంగా సౌకర్యవంతమైన నమూనాని అందిస్తుంది.
ఈ లో-లెవల్ సంగ్రహణను అర్థం చేసుకోవడం కేవలం విద్యాపరమైన వ్యాయామం కాదు; ఇది సాధారణ క్లయింట్లు మరియు సర్వర్ల నుండి ముందుకు వెళ్లడానికి మీకు అధికారం ఇచ్చే ఆచరణాత్మక నైపుణ్యం. ఇది ఏదైనా నెట్వర్క్ ప్రోటోకాల్ను పరిష్కరించడానికి, ఒత్తిడిలో పనితీరును ఆప్టిమైజ్ చేయడానికి నియంత్రణను, మరియు పైథాన్లో తదుపరి తరం అధిక-పనితీరు గల, అసమకాలిక సేవలను నిర్మించే సామర్థ్యాన్ని మీకు ఇస్తుంది. తదుపరిసారి మీరు సవాలు చేసే నెట్వర్కింగ్ సమస్యను ఎదుర్కొన్నప్పుడు, ఉపరితలం క్రింద ఉన్న శక్తిని గుర్తుంచుకోండి మరియు ట్రాన్స్పోర్ట్లు మరియు ప్రోటోకాల్ల సొగసైన ద్వయం కోసం చేరుకోవడానికి వెనుకాడకండి.